using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Framework.Serialization;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.Logic;
using IQIGame.Rendering;
using NetProtocol.Enum;
using UnityEngine;
using UnityEngine.Playables;
using Object = UnityEngine.Object;

namespace IQIGame.Onigao.GamePlay
{
    public partial class StoryManager : Singleton<StoryManager>
    {
        private Queue<StoryPlayParam> _waitingStory = new Queue<StoryPlayParam>();

        // 若没有给playable，则使用默认的playable
        private PlayableDirector _playableDirector;

        private StoryManager()
        {
        }

        public async UniTask StartPlayStoryById(int dialogID, int jumpCid = 0, TViewEntity interactingEntity = null, bool isPureDisplay = false)
        {
            var storyCfg = TableCenter.storyDialog.Get(dialogID);
            if (storyCfg == null)
            {
                return;
            }
            if (!this._playableDirector)
            {
                var rGo = GameObject.Find("story_PlayableDirector");
                if (!rGo)
                {
                    rGo = new GameObject("story_PlayableDirector");
                    Object.DontDestroyOnLoad(rGo);

                    this._playableDirector = rGo.AddComponent<PlayableDirector>();
                }
                else
                {
                    this._playableDirector = rGo.GetComponent<PlayableDirector>();
                }
            }
            await this.ShowStory(storyCfg.Name, this._playableDirector, jumpCid, interactingEntity);
        }

        public UniTask StartStory(string dialogName, PlayableDirector playable, int jumpCid)
        {
            if (!playable)
            {
                if (!this._playableDirector)
                {
                    var rGo = GameObject.Find("story_PlayableDirector");
                    if (!rGo)
                    {
                        rGo = new GameObject("story_PlayableDirector");
                        Object.DontDestroyOnLoad(rGo);

                        this._playableDirector = rGo.AddComponent<PlayableDirector>();
                    }
                    else
                    {
                        this._playableDirector = rGo.GetComponent<PlayableDirector>();
                    }
                }
                playable = this._playableDirector;
            }
            return this.ShowStory(dialogName, playable, jumpCid);
        }

        public async UniTask ShowStory(string dialogName, PlayableDirector playable, int jumpCid, TViewEntity conditionEntity = null, bool isPureDisplay = false)
        {
            var rParam = StoryPlayParam.Create(dialogName, playable, jumpCid, ManagerCenter.UI.GetEntityUI<UIStory>(), conditionEntity, isPureDisplay);
            await this.StartStory(rParam);
            ClassPool.Put(rParam);
        }

        private async UniTask StartStory(StoryPlayParam param)
        {
            if (this._loader == null)
            {
                this._loader = ResLoader.Create();
            }

#if UNITY_EDITOR
            if (!StoryClientUtil.IsEditorMode)
#endif
            {
                ManagerCenter.Entity.EnableGroup((int)EntityPoolGroupType.StoryNpc);
                ManagerCenter.Entity.EnableGroup((int)EntityPoolGroupType.Live2DEntity);
            }

            if (!this.isPlaying)
            {
                await this.ShowStory(param);
                // Check后不能执行逻辑，可能已经dispose了
                this.CheckNextWaitingStory();
                return;
            }
            this._waitingStory.Enqueue(param);
            await param.waitingTask.Task;
        }

        private void CheckNextWaitingStory()
        {
            if (this._waitingStory.TryDequeue(out var rParam)) //播放等待中的对话（对话未完成时开启的对话会进入等待状态，等待的任务转为_waitingTasks）
            {
                //该方法不能直接由StartStory结尾调用，因为要让上一个对话的任务正确结束
                //该方法应该由StartStory最上层调用者处理完所有逻辑后调用
                //当前这条对话的任务其实是以_waitingTasks内的任务为准的(见StartPlayStoryById(int storyId))
                // 这里启动时，需要保证isplaying = false
                this.StartStory(rParam).Forget();
            }
            else
            {
                // 在全部剧情队列都播放完成后，才去释放资源
                this.Dispose();
            }
        }

        public void Dispose()
        {
            // 清理旧队列强制结束
            while (this._waitingStory.TryDequeue(out var rItem))
            {
                if (rItem.waitingTask != null)
                {
                    rItem.waitingTask.TrySetResult();
                }
            }

            if (this._loader != null)
            {
                this._loader.Put2Pool();
                this._loader = null;
            }
#if UNITY_EDITOR
            if (!StoryClientUtil.IsEditorMode)
#endif
            {
                this._ui?.Close();
                this._ui = null;
                ManagerCenter.Entity.DisableGroup((int)EntityPoolGroupType.StoryNpc);
                ManagerCenter.Entity.DisableGroup((int)EntityPoolGroupType.Live2DEntity);
            }
            this._npcMap.Clear();
            if (this._playable)
            {
                this._playable.Stop();
            }
            this._playable = null;
            if (this._speedGuid != 0)
            {
                TimeScaleManager.Instance.RemoveTimeScale(this._speedGuid);
                this._speedGuid = 0;
            }
            if (this.blurRT != null)
            {
                RenderBlurUtil.ReleaseBlurRT(BlackGroundRTName);
                this.blurRT = null;
            }
        }

        public class StoryPlayParam : IReusableClass
        {
            public string storyName;
            public int jumpCid;
            public StoryConfig config;
            public UIStory ui;
            public PlayableDirector playable;
            public AutoResetUniTaskCompletionSource waitingTask;
            public TViewEntity conditionEntity;
            public bool isPureDisplay;
            
            public uint MaxStore => 5;

            public void OnReset()
            {
                this.storyName = null;
                this.jumpCid = 0;
                this.playable = null;
                this.waitingTask = null;
                this.ui = null;
                this.config = null;
                this.conditionEntity = null;
            }

            public void Result()
            {
                if (this.waitingTask != null)
                {
                    var rSource = this.waitingTask;
                    this.waitingTask = null;
                    rSource.TrySetResult();
                }
            }

            public static StoryPlayParam Create(string storyName, PlayableDirector playable, int jumpCid, UIStory ui, TViewEntity conditionEntity, bool isPureDisplay)
            {
                var rParam = ClassPool<StoryPlayParam>.Get();
                rParam.storyName = storyName;
                rParam.config = rParam.GetConfig(storyName);
                rParam.jumpCid = jumpCid;
                rParam.playable = playable;
                rParam.waitingTask = AutoResetUniTaskCompletionSource.Create();
                rParam.ui = ui;
                rParam.conditionEntity = conditionEntity;
                rParam.isPureDisplay = isPureDisplay;
                return rParam;
            }

            private StoryConfig GetConfig(string key)
            {
                var rLoader = ResLoader.Create();
                var rLanguageType = LanguageGlobal.language;
                // 这里分段处理参数数量降低到4个及以下省去string.Concat的params参数
                var rAssetPath = PathConstant.StoryCfgPath + rLanguageType.ToString() + string.Concat("/", key.ToLower(), ".bytes");
                var rAssetBytes = rLoader.LoadABAsset<TextAsset>(rAssetPath).bytes;
                var rByteBuf = new ByteBuf(rAssetBytes);
                var rConfig = rByteBuf.DeSerializeObject<StoryConfig>();
                rLoader.Put2Pool();
                return rConfig;
            }
        }

        private bool isPlaying => this.context != null;

        public StoryContext context;

        private UIStory _ui;
        // 播放中实际使用的playable
        private PlayableDirector _playable;
        // 关卡，主城时，进入剧情暂停所有entity
        private IGameplaySceneArea _curSceneArea;
        private bool _auto;
        private bool _autoWaiting;
        public int speedMultiplier { get; private set; } = 1;
        private int _speedGuid;
#if UNITY_EDITOR
        public Action<int> onShow;
#endif

        // 当前正在播放的task
        private AutoResetUniTaskCompletionSource _storyTask;
        private readonly List<UniTask> _showTask = new List<UniTask>();

        private ResLoader _loader;

        public async UniTask ShowStory(StoryPlayParam param)
        {
            StoryConfig config = param.config;
            UIStory ui = param.ui;
            PlayableDirector playable = param.playable;
            int jumpCid = param.jumpCid;

            this._storyTask = AutoResetUniTaskCompletionSource.Create();

            this._ui = ui;
            this._playable = playable;

            this.context = new StoryContext()
            {
                storyName = param.storyName,
                config = config,
                conditionEntity = param.conditionEntity
            };
            // 异步操作挪到创建上下文之后
            if (this._curSceneArea == null && config.has3D)
            {
                this._curSceneArea = GameplayAreaManager.Instance.curSceneArea;
                if (this._curSceneArea != null)
                {
                    await this._curSceneArea.SwitchPhasing(GameScenePhasing.ClientPlay);
                }
            }

            if (!param.isPureDisplay)
            {
                switch (config.storyType)
                {
                    case EStoryType.Level:
                        this.context.InitFunc(this.RepoLevelOptionSelection);
                        break;
                }
            }

            if (this.context.config.nodes.Count > 0)
            {
                var nShowIndex = 0;
                if (jumpCid != 0)
                {
                    nShowIndex = this.context.config.nodes.FindIndex((x) => x.id == jumpCid);
                    if (nShowIndex < 0)
                    {
                        nShowIndex = 0;
                    }
                }
                this.Show(nShowIndex).Forget();
            }
            await this._storyTask.Task;
            this.context = null;
            param.Result();
        }

        public void Next()
        {
            if (this.context.curNode.customConfig.TryGetValue(EStoryNodeType.End, out var rEnd))
            {
                this.End().Forget();
                return;
            }
            else if (this.context.curNode.customConfig.ContainsKey(EStoryNodeType.Option))
            {
                return;
            }
            if (this.context.curNode.directJumpID > 0)
            {
                this.Jump(this.context.curNode.directJumpID);
            }
        }

        public void Jump(int id)
        {
            if (this.context != null)
            {
                var nIndex = this.context.config.GetIndex(id);
                if (nIndex >= 0)
                {
                    this.Show(nIndex).Forget();
                }
            }
        }

        public void Skip()
        {
            if (this.context != null)
            {
                var rCurNode = this.context.curNode;
                while (rCurNode != null)
                {
                    var nNextID = 0;
                    var nSelectIndex = -1;
                    if (rCurNode.customConfig.ContainsKey(EStoryNodeType.End))
                    {
                        break;
                    }
                    if (rCurNode.customConfig.TryGetValue(EStoryNodeType.Option, out var rOptionConfig))
                    {
                        if (rOptionConfig is StoryNode_Option rOption)
                        {
                            if (!rOption.stopSkip)
                            {
                                // 选项默认选第一个
                                nSelectIndex = 0;
                                this.context.choose.Add(nSelectIndex);
                                nNextID = rOption.choose[nSelectIndex].jumpID;
                            }
                            else
                            {
                                if (this.context.curNode != rCurNode)
                                {
                                    this.context.history.RemoveAt(this.context.history.Count - 1);
                                    this.Jump(rCurNode.id);
                                }
                                return;
                            }
                        }
                    }
                    else
                    {
                        nNextID = rCurNode.directJumpID;
                    }
                    if (nNextID > 0)
                    {
                        var nIndex = this.context.config.GetIndex(nNextID);
                        if (nIndex >= 0)
                        {
                            this.context.history.Add((nIndex, nSelectIndex));
                            rCurNode = this.context.config.nodes[nIndex];
                            continue;
                        }
                    }
                    rCurNode = null;
                }
            }
            this.End().Forget();
        }

        private async UniTaskVoid End()
        {
            if (this._curSceneArea != null)
            {
                await this._curSceneArea.SwitchPhasing(GameScenePhasing.Main);
                this._curSceneArea = null;
            }
            await this.context.SendEnd();

#if UNITY_EDITOR
            if (!StoryClientUtil.IsEditorMode)
            {
            }
            else
#endif
            {
                this.context = null;
            }

            if (this._storyTask != null)
            {
                // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了。
                // 若置空不及时导致了多次source.TrySetResult，会导致池内数据异常
                var rSource = this._storyTask;
                this._storyTask = null;
                rSource.TrySetResult();
            }
        }

        const string BlackGroundRTName = "UIStoryBackGround";
        public RenderTexture blurRT { get; private set; }
        private async UniTask Show(int index)
        {
            this.context.curIndex = index;
            this.context.history.Add((index, -1));
            var rNode = this.context.config.nodes[index];
            this._autoWaiting = false;
            if (this._ui == null)
            {
                // TODO 后续修改为内置
                if (this.blurRT != null)
                {
                    RenderBlurUtil.ReleaseBlurRT(BlackGroundRTName);
                    this.blurRT = null;
                }
                this.blurRT = await RenderBlurUtil.GetBlurTexAsyncReadBack(BlackGroundRTName, GameRender.CameraType.MainCamera); 
                await ManagerCenter.UI.ShowWindowAsync<UIStory>();
                this._ui = ManagerCenter.UI.GetEntityUI<UIStory>();
                this._ui.Clear();
            }

            if (!this._ui.isActive)
            {
                this._ui.Show();
            }

            this._showTask.Clear();
            this._showTask.Add(this._ui.ShowStory(rNode));
            // 3D , BGM , Video
            var rConfig = this.context.curNode.customConfig;
            foreach (var rPair in rConfig)
            {
                switch (rPair.Value)
                {
                    case StoryNode_Timeline rTimeline:
                        _showTask.Add(this.PlayTimeline(rTimeline.timeline));
                        break;
                    case StoryNode_Remove3DNpc rRemove:
                        if (this._npcMap.TryGetValue(rRemove.npc, out var rNpc))
                        {
                            ClassPool.Put(rNpc);
                            this._npcMap.Remove(rRemove.npc);
                        }
                        break;
                    case StoryNode_Bgm rBgm:
                        switch (rBgm.bgmType)
                        {
                            case StoryNode_Bgm.EStoryBgmType.Add:
                                ManagerCenter.Audio.AddBgm(rBgm.bgm);
                                break;
                            case StoryNode_Bgm.EStoryBgmType.Switch:
                                ManagerCenter.Audio.SwitchBgm(rBgm.bgm);
                                break;
                            case StoryNode_Bgm.EStoryBgmType.Stop:
                                ManagerCenter.Audio.RemoveBgm(rBgm.bgm);
                                break;
                        }
                        break;
                    case StoryNode_Video rVideo:
                        var rPath = "Story/" + rVideo.video;
                        this._showTask.Add(this.PlayVideo(rPath));
                        break;
                }
            }
#if UNITY_EDITOR
            this.onShow?.Invoke(index);
#endif
            await UniTask.WhenAll(_showTask);
            this._showTask.Clear();

            if (this.context == null || rConfig != this.context.curNode.customConfig)
            {
                // 这种情况说明等待期间已经next了
                return;
            }

            // 视频会自动跳下一个
            if (rConfig.ContainsKey(EStoryNodeType.Video))
            {
                this.Next();
            }
            // 选项停止自动
            else if (this._auto && !rConfig.ContainsKey(EStoryNodeType.Option))
            {
                this._autoWaiting = true;
                await UniTask.Delay((int)(this.context.curNode.autoWait * 1000));
                if (this._autoWaiting)
                {
                    this.Next();
                }
            }
        }

        public void OnOptionChoose(int index)
        {
            var rHistory = this.context.history;
            if (rHistory.Count > 0 && rHistory[^1].Item1 == this.context.curIndex)
            {
                this.context.history[^1] = (this.context.curIndex, index);
            }
            this.context.choose.Add(index);
        }

        public bool Auto(bool auto)
        {
            this._auto = auto;
            if (this._auto && this._showTask.Count <= 0)
            {
                this.Next();
            }
            return this._auto;
        }

        #region Rep & Check Action

        private UniTask RepoLevelOptionSelection(List<int> selectionIndexes)
        {
#if UNITY_EDITOR
            if (StoryClientUtil.IsEditorMode)
            {
                return default;
            }
#endif
            return CLevelLogicModule.NetCore.CS_RepoStoryDialogSelection(selectionIndexes);
        }

        private async UniTask PlayVideo(string path)
        {
            await CommonVideoPlayManager.Instance.PlayVideo(path);

            CommonVideoPlayManager.Instance.StopVideo();
        }

        public int SetSpeed(int targetSpeed)
        {
            this.speedMultiplier = targetSpeed;
            if (this._speedGuid != 0)
            {
                TimeScaleManager.Instance.RemoveTimeScale(this._speedGuid);
            }
            this._speedGuid = TimeScaleManager.Instance.AddTimeScale(this.speedMultiplier, ETimeScaleType.StoryAcc);
            return this.speedMultiplier;
        }

        #endregion
    }
}